//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#if !defined(__KMARSHAL_H__)
#define __KMARSHAL_H__

#include <crust.h>

#define _MARSHAL_DEBUG  1

#define MSHDBG_NORMAL           1
#define MSHDBG_CREF             2
#define MSHDBG_WARNING          3
#define MSHDBG_ERROR            4
#define MSHDBG_NONE             100

#define MSHDBG_LEVEL              MSHDBG_ERROR

#if defined(_DEBUG) || defined(_MARSHAL_DEBUG)
#define MARSHAL_DBGOUT(level, exp) \
    ((level) >= MSHDBG_LEVEL ? (void)(exp) : (void)0)
#else
#define MARSHAL_DBGOUT(level, exp)
#endif // _MARSHAL_DEBUG

#if defined(_GNUC)
#if !defined(__PACKED__)
#define __PACKED__              __attribute__((packed))
#endif // __PACKED__
#else
#define __PACKED__
#endif

#define MARSHAL_MAGIC           0x7a79636f

#define MSH_MAX_METHODS         (128 + 3)

extern UInt32 g_marshalVtbl[MSH_MAX_METHODS];

#ifdef _EVC
#pragma     pack(1)
#endif

typedef struct __PACKED__ MarshalHeader
{
    UInt32              m_uMagic;           // magic number
    oid_t                 m_oid;              // server oid_t
    Int16               m_hInterfaceIndex;  // interface index in object
    Int16               m_hMethodIndex;     // method index in interface
    PContext            m_pContext;         // context hint
    UInt32              m_uInSize;          // size of in data
    UInt32              m_uOutSize;         // size of out data
} MarshalHeader;

#ifdef _EVC
#pragma     pack()
#endif

#define DECL_ADDREF_HEADER(h, oid, ctx)     \
    MarshalHeader h = {         \
        MARSHAL_MAGIC,          \
        oid,                    \
        0,                      \
        1,                      \
        ctx,                    \
        sizeof(MarshalHeader),  \
        0                       \
    }

#define DECL_RELEASE_HEADER(h, oid, ctx)    \
    MarshalHeader h = {         \
        MARSHAL_MAGIC,          \
        oid,                    \
        0,                      \
        2,                      \
        ctx,                    \
        sizeof(MarshalHeader),  \
        0                       \
    }

#ifdef _x86

inline UInt32 CalcMethodIndex(UInt32 uCallerAddr)
{
    return (uCallerAddr - PROXY_ENTRY_BASE) >> PROXY_ENTRY_SHIFT;
}

// void GET_REG(register, UInt32 v);
//
#define GET_REG(reg, v)                 \
    ASM("mov    %%"#reg", %0;"          \
        :"=m"(v)                        \
    )

// void SET_REG(register, UInt32 v);
//
#define SET_REG(reg, v)                 \
    ASM("mov    %0, %%"#reg";"          \
        ::"m"(v)                        \
    )

#define SYS_PROXY_EXIT(ec, pret, argc)  \
    do {                                \
        UInt32 n;                         \
        n = *(UInt32 *)(pret);            \
        n &= PROXY_ENTRY_MASK;          \
        n += PROXY_ENTRY_BASE + ((argc) << PROXY_ENTRY_SHIFT);  \
        *(UInt32 *)(pret) = n;            \
        return (ec);                    \
    } while (0)

#define STUB_INVOKE_METHOD(ec, puArgs, addr) \
    ASM("call   *%2;"                   \
        "mov    %%eax, %0;"             \
        "mov    %1, %%esp;"             \
        :"=m"(ec)                       \
        :"m"(puArgs), "m"(addr)         \
    )

#elif defined(_arm)

#define GET_REG(reg, v)                 \
    ASM("str    "#reg", %0;"            \
        :"=m"(v))

#define SET_REG(reg, v)                 \
    ASM("ldr    "#reg", %0;"            \
        : :"m"(v))

#define SYS_PROXY_EXIT(ec, pret, argc)  \
    do {                                \
        return (ec);                    \
    } while (0)

#define STUB_INVOKE_METHOD1(ec, puArgs, addr) \
    ASM("mov    lr, %1;"                \
        "ldr    r0, [lr];"              \
        "ldr    r1, [lr, #4];"          \
        "ldr    r2, [lr, #8];"          \
        "ldr    r3, [lr, #12];"         \
        "adr    lr, __StubInvokeNext1;" \
        "ldr    pc, %2;"                \
        "__StubInvokeNext1:"            \
        "str    r0, %0;"                \
        :"=m"(ec) :"r"(puArgs), "m"(addr))

#define STUB_INVOKE_METHOD2(ec, puArgs, addr) \
    ASM("mov    lr, %1;"                \
        "ldr    r0, [lr];"              \
        "ldr    r1, [lr, #4];"          \
        "ldr    r2, [lr, #8];"          \
        "ldr    r3, [lr, #12];"         \
        "add    sp, lr, #16;"           \
        "adr    lr, __StubInvokeNext2;" \
        "ldr    pc, %2;"                \
        "__StubInvokeNext2:"            \
        "str    r0, %0;"                \
        :"=m"(ec) :"r"(puArgs), "m"(addr))

#elif defined(_mips)

inline UInt32 CalcMethodIndex(UInt32 uCallerAddr)
{
    return (uCallerAddr - PROXY_ENTRY_BASE) >> PROXY_ENTRY_SHIFT;
}

#if defined(_GNUC)
#define GET_REG(reg, v)                 \
    ASM("sw "#reg", %0;":"=m"(v))

#define SET_REG(reg, v)                 \
    ASM("lw "#reg", %0;"::"m"(v))

#define SYS_PROXY_EXIT(ec, pret, argc)  \
    do {                                \
        return (ec);                    \
    } while (0)

#define STUB_INVOKE_METHOD(ec, puArgs, addr)    \
    ASM(                                        \
        "move   $29, %1;"                       \
        "lw     $4, 0x0($29);"                  \
        "lw     $5, 0x4($29);"                  \
        "lw     $6, 0x8($29);"                  \
        "lw     $7, 0xc($29);"                  \
        "jal    %2;"                            \
        "move   %0, $2;"                        \
        :"=&r"(ec):"r"(puArgs), "r"(addr))
#elif defined(_EVC)
#define GET_REG(reg, v)                 \
    ASM("sw "#reg", 0(%0);", &v)

#define SET_REG(reg, v)                 \
    ASM("move "#reg", %0;", v)

#define SYS_PROXY_EXIT(ec, pret, argc)  \
    do {                                \
        return (ec);                    \
    } while (0)

#define STUB_INVOKE_METHOD(ec, puArgs, addr)    \
    ASM(                                        \
        "addiu  sp, sp, -0x20;"                 \
        "sw     s0, 0x10(sp);"                  \
        "sw     s1, 0x14(sp);"                  \
        "sw     s2, 0x18(sp);"                  \
        "or     s0, a0, zero;"                  \
        "or     s1, sp, zero;"                  \
        "or     s2, a2, zero;"                  \
        "or     sp, a1, zero;"                  \
        "lw     a0, 0x0(sp);"                   \
        "lw     a1, 0x4(sp);"                   \
        "lw     a2, 0x8(sp);"                   \
        "lw     a3, 0xc(sp);"                   \
        "jal    s2;"                            \
        "sw     v0, 0(s0);"                     \
        "or     sp, s1, zero;"                  \
        "lw     s0, 0x10(sp);"                  \
        "lw     s1, 0x14(sp);"                  \
        "lw     s2, 0x18(sp);"                  \
        "addiu  sp, sp, 0x20;"                  \
        , &ec, puArgs, addr)
#else
#error unknown compiler
#endif
#else
#error unknown architecture
#endif

#ifdef _EVC
#pragma     pack(1)
#endif

typedef struct __PACKED__ InterfacePack {
    oid_t             m_oid;      // must be first member
    ProcessId       m_pid;      // id of server process
    EMuid           m_clsid;    // clsid of object
    UInt32          m_uIndex;   // interface index in class
    UInt32          m_pClsEntry; // class information of object
}   InterfacePack;

#ifdef _EVC
#pragma     pack()
#endif

#define INVALID_PID     0x00000000  // invalid pid value
#define KERNEL_PID      0x00000001  // pid value of kernel object

extern class CKernel *g_pCKernel;
bool_t RegisterKernelInterface();
void RelocateClassInfo(CIClassInfo *pInClsInfoData,
                        CIClassInfo * pClsInfo);

// TODO:
INLINE void *GetUnalignedPtr(void *pPtr)
{
#ifdef _x86
    return *(void **)pPtr;
#else
    union
    {
        void    *p;
        byte_t  bytes[4];
    } u;
    u.bytes[0] = ((byte_t *)pPtr)[0];
    u.bytes[1] = ((byte_t *)pPtr)[1];
    u.bytes[2] = ((byte_t *)pPtr)[2];
    u.bytes[3] = ((byte_t *)pPtr)[3];

    return u.p;
#endif
}

INLINE Boolean IsUserECode(ECode ec)
{
    return (((ec) == E_ERROR_STRING) ||
            ((ec) == E_ERROR_STRING_A) ||
            ((ec) == E_ERROR_URL));
}

#define MSH_ALIGN_4(n)  RoundUp2((n), 4)

#define MSH_NULL        ((UInt32)0)
#define MSH_NOT_NULL    ((UInt32)-2)

EXTERN_C const InterfaceID EIID_CLASS_INFO;

#define STATUS_WIN32_ERROR(u) ((ECode)((u)|0x80070000))

#define E_ENTRY_NOT_FOUND         STATUS_WIN32_ERROR(1761)

#if defined(_DEBUG) || defined(_MARSHAL_DEBUG)
extern int _DumpGUID(REIID riid);
extern int _DumpCLSID(RClassID rclsid);
extern int _DumpBin(const void *pv, int size);

#define DUMP_GUID(riid)         _DumpGUID(riid)
#define DUMP_CLSID(rclsid)      _DumpCLSID(rclsid)
#define DUMP_BIN(v, s)          _DumpBin(v, s)
#else

#define DUMP_GUID(riid)
#define DUMP_CLSID(rclsid)
#define DUMP_BIN(v, s)
#endif // _DEBUG

#endif // __KMARSHAL_H__
